package mblog.web.interceptor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import mblog.core.persist.dao.UserDao;
import mblog.core.utils.BeanMapUtils;
import mtons.modules.lang.Const;
import mtons.modules.pojos.UserProfile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.faxsun.core.persist.entity.UserPO;
import com.faxsun.web.pojos.FSUserProfile;

/**
 * 
 * @author zhilin
 * @since 2016-06-12
 */
@Component
public class RememberUserInterceptor extends HandlerInterceptorAdapter {
	private static final Logger log = LoggerFactory
			.getLogger(RememberUserInterceptor.class);

	@Autowired
	UserDao userDao;

	private String loginUrl = "/login";
	private static final String COOKIE_KEY = "TOKEN";

	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// 如果是登录请求，在处理登录成功后修改数据库的login_status
		// 如果是登出操作，在登出处理结束之后修改数据库中的login_status
		// 如果是其他操作不做处理
		String path = request.getRequestURL().toString();
		HttpSession session = request.getSession(false);
		if (session == null)
			return;
		Cookie[] cookies = request.getCookies();
		UserProfile profile = (UserProfile) session
				.getAttribute(Const.KEY_LOGIN);
		if (path.contains(loginUrl) && profile != null) {
			String username = profile.getUsername();
			UserPO user = userDao.get(username);
			String token = username;// UUID.randomUUID().toString().replace("-",
									// "");
			user.setToken(token);
			user.setLoginStatus(LoginStatus.LOGIN.value());
			userDao.update(user);
			// response.addCookie(new Cookie(COOKIE_KEY, user.getToken()));
			this.addCookie(cookies, token, response);
			session.setAttribute("token", token);

		}
		/**
		 * 通过session存储ajax login生成的token，在随后的非ajax操作中设置cookies值
		 */
		if (session.getAttribute("token") != null) {
			if (this.getToken(request.getCookies()) == null) {
				this.addCookie(cookies, session.getAttribute("token")
						.toString(), response);
			}

		}

		return;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		// 如果是第一次访问网站，判断cookies，和userProfile，
		// 如果userProfile存在，则pass
		// 如果userProfile不存在，cookies存在，构造userProfile
		// 如果userprofile不存在，cookies不存在，不做任何操作,后续拦截器导向登录页面
		log.debug("RememberUserInterceptor is processing"
				+ request.getRequestURL().toString());
		String path = request.getRequestURL().toString();
		Cookie[] cookies = request.getCookies();
		HttpSession session = request.getSession();
		UserProfile userProfile = (UserProfile) session
				.getAttribute(Const.KEY_LOGIN);
		if (path.contains(loginUrl)) {
			log.debug("[Passed]Login request!");
			this.expireCookies(request.getCookies(), response);
			return true;
		}

		if (path.matches("^.*/logout$")) {
			String token = this.getToken(cookies);
			if (token == null) {
				log.debug("User not login,but try logout!");
				return true;
			}
			UserPO user = userDao.getByToken(token);
			session.setAttribute("_valid", null);
			if (user == null) {
				log.debug("User is null for token:" + token);
				this.expireCookies(cookies, response);
				return true;
			}
			user.setLoginStatus(LoginStatus.LOGOUT.value());
			userDao.update(user);
			this.expireCookies(cookies, response);
			return true;
		}

		if (userProfile != null) {
			log.debug("UserProfile detected!");
			// String username = userProfile.getUsername();
			long id = userProfile.getId();
			UserPO user = userDao.get(id);
			if (!(user.getToken() != null && user.getLoginStatus() == LoginStatus.LOGIN
					.value())) {
				log.debug("User Profile exists, but not token or wrong login status");
				String username = userProfile.getUsername();
				if (StringUtils.isEmpty(username)) {
					username = "";
				}
				user.setToken(/* UUID.randomUUID().toString().replace("-", "") */new Md5PasswordEncoder()
						.encodePassword(username, id));
				user.setLoginStatus(LoginStatus.LOGIN.value());
				userDao.update(user);
				// response.addCookie(new Cookie(COOKIE_KEY, user.getToken()));
				this.addCookie(request.getCookies(), user.getToken(), response);
			}
			return true;
		}
		// 如果userProfile和token都不存在，那么系统认为用户未登录，执行登录操作
		String token = getToken(cookies);
		if (token == null) {
			this.expireCookies(cookies, response);
			log.debug("no token and profile,redirect to login page!");
			return true;
		}
		// 如果userProfile==null,但是cookies存在
		// 那么重新构造userProfile
		if (userProfile == null) {
			UserPO user = userDao.getByToken(token);
			if (user == null) {
				log.debug("wrong token info,redirect to login page!");
				this.expireCookies(cookies, response);
				return true;
			}
			log.info("re-construct user profile info!");
			if (user.getLoginStatus() == LoginStatus.LOGIN.value()) {
				UserProfile profile = BeanMapUtils.copyPassport(user);
				this.putProfile(profile, session);
			}
		}
		return true;
	}

	/**
	 * Retrieve token value from cookies
	 * 
	 * @param cookies
	 * @return
	 */
	private String getToken(Cookie[] cookies) {
		if (cookies == null)
			return null;
		for (Cookie c : cookies) {
			if (c.getName().equals(COOKIE_KEY))
				return c.getValue();
		}
		return null;
	}

	/**
	 * Set cookies to expired and let client remove cookies.
	 * 
	 * @param cookies
	 * @param response
	 */
	private void expireCookies(Cookie[] cookies, HttpServletResponse response) {
		if (cookies == null)
			return;
		for (Cookie c : cookies) {
			if (c.getName().equals(COOKIE_KEY)) {
				c.setMaxAge(0);
				c.setPath("/");
				response.addCookie(c);
			}
		}
	}

	/**
	 * update existing cookies value of add new cookies with key named
	 * COOKIE_KEY
	 * 
	 * @param cookies
	 * @param value
	 * @param response
	 */
	private void addCookie(Cookie[] cookies, String value,
			HttpServletResponse response) {
		Cookie cookie = null;
		if (cookies != null) {
			for (Cookie c : cookies) {
				if (c.getName().equals(COOKIE_KEY)) {
					cookie = c;
				}
			}
		}
		if (cookie == null) {
			cookie = new Cookie(COOKIE_KEY, value);
		}
		cookie.setValue(value);
		cookie.setPath("/");
		response.addCookie(cookie);
	}

	/**
	 * 该段代码逻辑来自BaseController putProfile(UserProfile userProfile)
	 * 
	 * @param user
	 * @param session
	 */
	protected void putProfile(UserProfile user, HttpSession session) {
		session.setAttribute(Const.KEY_LOGIN, user);

		// 设置电商需要的登录信息
		if (user != null) {
			FSUserProfile fsUser = (FSUserProfile) user;
			session.setAttribute("_blc_overrideCustomerId", fsUser.getUserId());
			session.setAttribute("OK_TO_USE_SESSION", Boolean.TRUE);
			session.setAttribute("info_first_login", Boolean.TRUE);
		} else {
			session.removeAttribute("info_first_login");
			session.removeAttribute("_blc_overrideCustomerId");
		}
	}

	enum LoginStatus {
		LOGOUT(0), LOGIN(1);
		private int id;

		LoginStatus(int id) {
			this.id = id;
		};

		public int value() {
			return this.id;
		}
	}

}
